---
category: Basics
title: Loading Assets
order: -750
---

Threlte works seamlessly with Three.js loaders, and provides a hook,
[useLoader](/docs/reference/core/use-loader), to help solve common loading issues.
It caches results to prevent duplicate fetch/parse work and returns an async store,
[asyncWritable](/docs/reference/core/utilities#asyncwritable) for convenient usage in Svelte.

Caching cuts network requests, bandwidth, and memory, improving app performance.

<Tip type="info">
  This section assumes you placed your assets in your public folder or in a place in your
  application where you can import them easily.
</Tip>
<Tip type="tip">
  We recommend using SvelteKit's [asset function](https://svelte.dev/docs/kit/$app-paths#asset) to
  help catch common loading problems. There are other equivalent vite plugins if your not using
  SvelteKit.
</Tip>

## Models

Models come in many different formats, but `.gltf`s are usually the best fit
for the web. You can use Three.js' `GLTFLoader` to load a `.gltf` model.

```svelte
<script>
  import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
  import { useLoader } from '@threlte/core'

  const gltf = useLoader(GLTFLoader).load('/assets/model.gltf')
</script>

{#if $gltf}
  <T is={$gltf.scene} />
{/if}
```

The [`<GLTF/>`](/docs/reference/extras/gltf) component is roughly equivalent to
the example above.

### `useGltf` hook

`@threlte/extras` provides a handy hook for loading `.gltf` models called
[useGltf](/docs/reference/extras/use-gltf):

```svelte
<script>
  import { useGltf } from '@threlte/extras'
</script>

{#await useGltf('/assets/model.gltf') then gltf}
  <T is={gltf.scene} />
{/await}
```

### Adjusting and reusing models

You may run into the following challenges when working with models in Threlte:

- Your model may have multiple parts that you'd like to adjust individually but
  there's no easy way to declaritively achieve that with only one `<T/>`
  component.
- You can't seem to place multiple copies in your scene.

To address both of these issues, you can use Threlte's CLI tool
[@threlte/gltf](/docs/reference/gltf/getting-started) to generate a Svelte
component for your model. The generated component has `<T/>` components for all
of your models parts. [Adjust the component](/docs/learn/advanced/custom-abstractions) to your liking, then
import and reuse it as much as you'd like.

### Animations

Three.js uses [AnimationMixer](https://threejs.org/docs/index.html#api/en/animation/AnimationMixer)
to drive animations. Threlte provides a convenient
[useGltfAnimations](/docs/reference/extras/use-gltf-animations) hook for gltfs.
See the animation [Three.js example](https://threejs.org/examples/?q=animation)
for how to setup a model for your animation needs.

The Threlte docs also have an [animation example](/docs/examples/animation/animation-transitions) to help you get
started.

### Common mistakes

Sometimes you'll load a model and still just see a blank screen. Here are a few
common mistakes that can cause this.

1. Make sure your scene has lights.
2. Make sure the model is not too small or not too big for your camera to see
   it.
3. Make sure your camera is looking at the model
4. Does the model render in a viewer like [gltf-viewer](https://gltf-viewer.donmccurdy.com/)?

## Textures

The `TextureLoader` is another loader from Three.js that is used for textures.

```svelte
<script>
  import { TextureLoader } from 'three'
  import { useLoader } from '@threlte/core'

  const texture = useLoader(TextureLoader).load('/assets/texture.png')
</script>

{#if $texture}
  <T.MeshStandardMaterial map={$texture} />
{/if}
```

### `useTexture` hook

`@threlte/extras` provides a handy hook for loading textures called
[useTexture](/docs/reference/extras/use-texture):

```svelte
<script>
  import { useTexture } from '@threlte/extras'
</script>

{#await useTexture('/assets/texture.png') then texture}
  <T.MeshStandardMaterial map={texture} />
{/await}
```

### Multiple textures

Sometimes you'll want your materials to be composed of multiple textures.
`useLoader` provides a way to load multiple textures at once and
[spread](https://learn.svelte.dev/tutorial/spread-props) the loaded textures on
a material.

Loading two textures for the `map` and `normalMap` channels can be done like
this:

```ts
const textures = useLoader(TextureLoader).load({
  map: '/assets/texture.png',
  normalMap: '/assets/normal.png'
})
```

or with the `useTexture` hook:

```ts
const textures = useTexture({
  map: '/assets/texture.png',
  normalMap: '/assets/normal.png'
})
```

Then spread on a material:

```svelte
{#if $textures}
  <T.MeshStandardMaterial {...$textures} />
{/if}
```

If multiple textures are given, the promise only resolves once all textures
have loaded.

### Applying multiple textures to different mesh faces

To declaratively apply different textures to multiple faces of a `BoxGeometry`,
set the `attach` prop to a function.

```svelte
<T.Mesh>
  <T.BoxGeometry />
  <T.MeshStandardMaterial
    map={texture1}
    attach={({ parent, ref }) => {
      if (Array.isArray(parent.material)) {
        parent.material = [...parent.material, ref]
      } else {
        parent.material = [ref]
      }
    }}
  />
  <T.MeshStandardMaterial
    map={texture2}
    attach={({ parent, ref }) => {
      if (Array.isArray(parent.material)) {
        parent.material = [...parent.material, ref]
      } else {
        parent.material = [ref]
      }
    }}
  />
</T.Mesh>
```

## Other asset types

Threlte provides many components to help get started with many other asset
types (like [audio](/docs/reference/extras/audio)), but it doesn't have
components and hooks for all of them. Checkout [Three.js
examples](https://threejs.org/examples/) to see what models, techniques and
effects you can achieve, then use those examples as a guide for your own
[custom components](/docs/learn/advanced/custom-abstractions).

## Async loading

The return value from [useLoader](/docs/reference/core/use-loader) is an
`AsyncWritable` custom store. Its value will be `undefined` until the asset has
loaded.

Since the underlying store's value is a promise, you can use it within a Svelte
`#await` block:

```svelte
{#await $texture then value}
  <T.MeshStandardMaterial map={value} />
{/await}
```

Assets can also be loaded after initialization by calling the `loader.load`
method:

```svelte
<script>
  import { AudioLoader } from 'three'
  import { useLoader } from '@threlte/core'

  // Instantiate the loader at component initialization
  const loader = useLoader(AudioLoader)

  const onSomeEvent = async () => {
    // Load the asset when needed
    const audioBuffer = await loader.load('/assets/sound.mp3')
  }
</script>
```

The [Suspense](/docs/reference/extras/suspense) component can additionally help
orchestrate loading multiple assets before displaying your scene.

## Context Awareness

The `useLoader` hook, and other hooks like `useTexture`, use Svelte contexts.
The assets loaded with them are only available for child components of your
`<Canvas>` component.
